diff --git a/patterns/workflow-generation.json b/patterns/workflow-generation.json index e68807e..2fc4583 100644 --- a/patterns/workflow-generation.json +++ b/patterns/workflow-generation.json @@ -114,6 +114,28 @@ "browser": { "tool": "playwright", "requirement": "Enable Playwright CLI for browser automation" + }, + "agentic-workflows": { + "tool": "agentic-workflows", + "requirement": "Enable agentic-workflows tools to create meta-workflows that analyze and optimize agentic workflows" + }, + "lsp": { + "lsp": { + "typescript": { + "command": "typescript-language-server", + "args": [ + "--stdio" + ], + "fileExtensions": { + ".ts": "typescript", + ".tsx": "typescriptreact", + ".js": "javascript", + ".mjs": "javascript", + ".cjs": "javascript" + } + } + }, + "requirement": "Detect the programming languages used in the repository and targeted by the workflow, then configure matching Language Server Protocol tools and add steps to install them" } }, "pre_steps": { diff --git a/src/js/workflow.js b/src/js/workflow.js index a98becd..ae9c147 100644 --- a/src/js/workflow.js +++ b/src/js/workflow.js @@ -172,16 +172,21 @@ function toolsetsFor(patterns, archetype) { generation.default_github_toolsets || []; } -function lspFor(patterns, archetype, engine) { - if (normalizeEngine(engine) !== 'copilot') return null; - const lsp = workflowDefinition(patterns, archetype).lsp || {}; - const validLsp = {}; - Object.entries(lsp).forEach(([language, config]) => { +function validLsp(lsp) { + const valid = {}; + Object.entries(lsp || {}).forEach(([language, config]) => { if (config && config.command && config.fileExtensions && Object.keys(config.fileExtensions).length) { - validLsp[language] = config; + valid[language] = config; } }); - return Object.keys(validLsp).length ? validLsp : null; + return valid; +} + +function lspFor(patterns, archetype, engine, extras) { + if (normalizeEngine(engine) !== 'copilot') return null; + const lsp = validLsp(workflowDefinition(patterns, archetype).lsp); + extras.forEach((extra) => Object.assign(lsp, validLsp(extra.lsp))); + return Object.keys(lsp).length ? lsp : null; } export function generateWorkflowFile(answers, patterns) { @@ -197,7 +202,7 @@ export function generateWorkflowFile(answers, patterns) { const safeOutputs = safeOutputsFor(answers, patterns); const inferred = inferCapabilities(answers.archetype, patterns); const extras = selectedExtras(answers, patterns); - const lsp = lspFor(patterns, answers.archetype, answers.engine); + const lsp = lspFor(patterns, answers.archetype, answers.engine, extras); let timeout = archetype.timeout_minutes || 30; const timeoutByTrigger = patterns.config_defaults && patterns.config_defaults.timeout_by_trigger; @@ -239,7 +244,10 @@ export function generateWorkflowFile(answers, patterns) { if (inferred.githubToolsets) { frontmatter += ` github:\n toolsets: [${ toolsetsFor(patterns, answers.archetype).join(', ') }]\n`; } - if (extras.some((extra) => extra.tool === 'cache-memory')) frontmatter += ' cache-memory:\n'; + extras.forEach((extra) => { + if (extra.tool === 'cache-memory') frontmatter += ' cache-memory:\n'; + if (extra.tool === 'agentic-workflows') frontmatter += ' agentic-workflows: true\n'; + }); if (inferred.browser || extras.some((extra) => extra.tool === 'playwright')) { frontmatter += ' playwright:\n mode: cli\n'; } diff --git a/src/wizard.json b/src/wizard.json index 6884ee5..aad2817 100644 --- a/src/wizard.json +++ b/src/wizard.json @@ -173,6 +173,20 @@ "description": "Enable Playwright CLI for browser automation", "icon": "device-desktop", "summary": "browser access" + }, + { + "id": "agentic-workflows", + "label": "Analyze workflows", + "description": "Create meta-workflows that analyze and optimize agentic workflows", + "icon": "tools", + "summary": "agentic workflow analysis" + }, + { + "id": "lsp", + "label": "Use language services", + "description": "Use language service protocol tools for symbol-aware code analysis", + "icon": "terminal", + "summary": "language service access" } ] }, diff --git a/test/a11y.test.js b/test/a11y.test.js index 1367589..63ab994 100644 --- a/test/a11y.test.js +++ b/test/a11y.test.js @@ -174,7 +174,9 @@ describe('Primer iconography', () => { expect(wizard.steps.extra.options.map((option) => option.icon)).toEqual([ 'cache', 'graph', - 'device-desktop' + 'device-desktop', + 'tools', + 'terminal' ]); }); diff --git a/test/summary.test.js b/test/summary.test.js index 2c274b3..02686ce 100644 --- a/test/summary.test.js +++ b/test/summary.test.js @@ -56,10 +56,12 @@ describe('buildWorkflowSummary', () => { it('summarizes optional extras separately from the selected engine', () => { const summary = buildWorkflowSummary(answers({ engine: 'copilot', - extras: ['memory', 'charts', 'browser'] + extras: ['memory', 'charts', 'browser', 'agentic-workflows', 'lsp'] }), patterns, wizardConfig); - expect(summary.extras.value).toBe('memory between runs, chart generation, and browser access'); + expect(summary.extras.value).toBe( + 'memory between runs, chart generation, browser access, agentic workflow analysis, and language service access' + ); expect(summary.extras.complete).toBe(true); expect(summary.engine.value).toBe('Copilot'); }); diff --git a/test/workflow.test.js b/test/workflow.test.js index 03fee88..dea3c11 100644 --- a/test/workflow.test.js +++ b/test/workflow.test.js @@ -183,6 +183,22 @@ describe('inferCapabilities', () => { ); expect(md).not.toContain('lsp:\n'); }); + + it('adds selected agentic-workflows and LSP extras for Copilot workflows', () => { + const md = generateWorkflowFile( + answers({ + extras: ['agentic-workflows', 'lsp'], + triggers: ['schedule'], + outputs: ['create-issue'] + }), + patterns + ); + + expect(md).toContain(' agentic-workflows: true\n'); + expect(md).toContain('lsp:\n typescript:\n command: typescript-language-server\n'); + expect(md).toContain(' ".tsx": typescriptreact\n'); + expect(md).toContain('network:\n allowed:\n - defaults\n - github\n - node\n'); + }); }); describe('buildTriggerYaml', () => { @@ -449,6 +465,14 @@ describe('generateAgentPrompt', () => { expect(prompt).toContain('- Enable Playwright CLI for browser automation\n'); }); + it('directs LSP workflows to detect relevant languages and install matching tooling', () => { + const prompt = generateAgentPrompt(answers({ extras: ['lsp'] }), patterns); + + expect(prompt).toContain( + '- Detect the programming languages used in the repository and targeted by the workflow, then configure matching Language Server Protocol tools and add steps to install them\n' + ); + }); + it('includes the selected engine requirement', () => { const prompt = generateAgentPrompt(answers({ engine: 'claude' }), patterns); expect(prompt).toContain('- Engine: claude\n');