@aiwg/cli 2026.8.0 → 2026.8.2

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 (70) hide show
  1. package/README.md +33 -0
  2. package/agentic/code/providers/capability-matrix.yaml +511 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +120 -0
  4. package/agentic/code/providers/model-catalog.v1.json +96 -0
  5. package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
  6. package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
  7. package/bin/aiwg.mjs +14 -10
  8. package/dist/src/api/index.d.ts +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/artifacts/cli.js +2 -0
  11. package/dist/src/artifacts/types.js +4 -0
  12. package/dist/src/auth/client.js +209 -0
  13. package/dist/src/auth/config.js +38 -0
  14. package/dist/src/auth/credential-store.js +141 -0
  15. package/dist/src/auth/resource-credentials.js +25 -0
  16. package/dist/src/auth/types.js +2 -0
  17. package/dist/src/channel/manager.mjs +5 -5
  18. package/dist/src/cli/handlers/auth.js +125 -0
  19. package/dist/src/cli/handlers/help.js +1 -0
  20. package/dist/src/cli/handlers/index.js +3 -1
  21. package/dist/src/cli/handlers/install.js +42 -4
  22. package/dist/src/cli/handlers/marketplace.js +375 -122
  23. package/dist/src/cli/handlers/resource-versions.js +2 -0
  24. package/dist/src/cli/handlers/sessions.js +23 -5
  25. package/dist/src/cli/handlers/subcommands.js +10 -1
  26. package/dist/src/cli/handlers/use.js +342 -43
  27. package/dist/src/config/gitignore.js +1 -0
  28. package/dist/src/extensions/commands/definitions.js +19 -0
  29. package/dist/src/marketplace/exchange.js +602 -0
  30. package/dist/src/marketplace/provenance-types.js +19 -0
  31. package/dist/src/marketplace/provenance.js +834 -0
  32. package/dist/src/memory/canonical-context.js +342 -0
  33. package/dist/src/memory/context-pack.js +282 -0
  34. package/dist/src/memory/index.js +4 -0
  35. package/dist/src/memory/intake.js +118 -0
  36. package/dist/src/packages/adapters/git.js +79 -29
  37. package/dist/src/packages/package-discovery.js +81 -0
  38. package/dist/src/packages/package-registry.js +2 -0
  39. package/dist/src/packages/registry.js +119 -20
  40. package/dist/src/resources/resolver.js +1 -0
  41. package/dist/src/resources/web-release.d.ts +3 -1
  42. package/dist/src/resources/web-release.js +14 -6
  43. package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
  44. package/dist/src/serve/fleet-mission-conductor.js +293 -0
  45. package/dist/src/sessions/index.js +1 -0
  46. package/dist/src/sessions/output-registration.js +338 -0
  47. package/dist/src/sessions/promotion.js +73 -2
  48. package/dist/src/sessions/repository.js +2 -1
  49. package/dist/src/update/notifier.mjs +13 -2
  50. package/package.json +8 -1
  51. package/tools/_resolve-impl.mjs +74 -0
  52. package/tools/agents/deploy-agents.mjs +962 -0
  53. package/tools/agents/providers/base.mjs +2954 -0
  54. package/tools/agents/providers/claude.mjs +711 -0
  55. package/tools/agents/providers/codex.mjs +699 -0
  56. package/tools/agents/providers/copilot.mjs +659 -0
  57. package/tools/agents/providers/cursor.mjs +714 -0
  58. package/tools/agents/providers/factory.mjs +1130 -0
  59. package/tools/agents/providers/hermes.mjs +663 -0
  60. package/tools/agents/providers/hook-capabilities.mjs +85 -0
  61. package/tools/agents/providers/model-role.mjs +56 -0
  62. package/tools/agents/providers/openclaw-translator.mjs +348 -0
  63. package/tools/agents/providers/openclaw.mjs +680 -0
  64. package/tools/agents/providers/opencode.mjs +675 -0
  65. package/tools/agents/providers/openhuman.mjs +292 -0
  66. package/tools/agents/providers/warp.mjs +413 -0
  67. package/tools/agents/providers/windsurf.mjs +748 -0
  68. package/tools/commands/deploy-prompts-codex.mjs +336 -0
  69. package/tools/plugin/package-plugins.mjs +1013 -0
  70. package/tools/skills/deploy-skills-codex.mjs +571 -0
@@ -0,0 +1,748 @@
1
+ /**
2
+ * Windsurf Provider (EXPERIMENTAL)
3
+ *
4
+ * Deploys agents to Windsurf format with aggregated AGENTS.md and trigger-frontmatter rules.
5
+ *
6
+ * Deployment paths:
7
+ * - Output: AGENTS.md (aggregated agents)
8
+ * - Output: .windsurf/rules/aiwg-orchestration.md (orchestration context, trigger: always_on)
9
+ * - Output: .windsurfrules (deprecated stub — retained for backward compat only)
10
+ * - Workflows: .windsurf/workflows/ (commands as workflows)
11
+ * - Skills: .windsurf/skills/ (discrete skill directories)
12
+ * - Skills: .agents/skills/ (cross-agent compatibility path)
13
+ * - Rules: .windsurf/rules/ (discrete rule files with trigger frontmatter)
14
+ *
15
+ * Special features:
16
+ * - Aggregated output (all agents in single AGENTS.md)
17
+ * - Plain markdown format (no YAML frontmatter)
18
+ * - Capabilities tags for tools
19
+ * - Workflow format for commands
20
+ * - Conventional skills and rules deployment
21
+ * - EXPERIMENTAL: Untested, may require adjustments
22
+ */
23
+
24
+ import realFs from 'fs';
25
+ import { createRequire } from 'module';
26
+ const _require = createRequire(import.meta.url);
27
+ let fs;
28
+ try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
29
+ import path from 'path';
30
+ import {
31
+ ensureDir,
32
+ listMdFiles,
33
+ listMdFilesRecursive,
34
+ initializeFrameworkWorkspace,
35
+ getAddonAgentFiles,
36
+ getAddonCommandFiles,
37
+ getAddonRuleFiles,
38
+ getAddonSkillDirs,
39
+ listSkillDirs,
40
+ deploySkillDir,
41
+ deploySkillsWithKernelRouting,
42
+ isKernelSkill,
43
+ pruneStaleAiwgSkills,
44
+ computeAllKernelNames,
45
+ deployFiles,
46
+ normalizeDeploymentMode,
47
+ collectFrameworkArtifacts,
48
+ listOnDemandRuleFiles,
49
+ writeOnDemandRuleIndex,
50
+ cleanupOldRuleFiles,
51
+ filterCommandsAgainstSkills,
52
+ deploySoulCompanions
53
+ } from './base.mjs';
54
+
55
+ // ============================================================================
56
+ // Provider Configuration
57
+ // ============================================================================
58
+
59
+ export const name = 'windsurf';
60
+ export const aliases = [];
61
+
62
+ export const paths = {
63
+ agents: '.windsurf/agents/', // Discrete mirrors alongside AGENTS.md
64
+ commands: '.windsurf/workflows/',
65
+ // Skills sequestered under .windsurf/.aiwg/skills/ — index-driven discovery (#1212).
66
+ skills: '.windsurf/.aiwg/skills/',
67
+ crossAgentSkills: '.agents/skills/', // Cross-agent compatibility path (#576)
68
+ rules: '.windsurf/rules/'
69
+ };
70
+
71
+ // Kernel skills (always-loaded) deploy to the platform-native dir.
72
+ export const kernelSkillsPath = '.windsurf/skills/';
73
+
74
+ export const support = {
75
+ agents: 'aggregated', // Agents aggregated into AGENTS.md
76
+ commands: 'native', // Native workflow/commands support
77
+ skills: 'conventional', // Conventional discrete deployment
78
+ rules: 'conventional' // Conventional discrete deployment
79
+ };
80
+
81
+ export const capabilities = {
82
+ skills: true,
83
+ rules: true,
84
+ aggregatedOutput: true, // All content in single file
85
+ yamlFormat: false
86
+ };
87
+
88
+ // ============================================================================
89
+ // Warning Display
90
+ // ============================================================================
91
+
92
+ function displayWarning() {
93
+ console.log('\n' + '='.repeat(70));
94
+ console.log('[EXPERIMENTAL] Windsurf provider support is experimental and untested.');
95
+ console.log('Please report issues: https://github.com/jmagly/aiwg/issues');
96
+ console.log('='.repeat(70) + '\n');
97
+ }
98
+
99
+ // ============================================================================
100
+ // Content Transformation
101
+ // ============================================================================
102
+
103
+ /**
104
+ * Transform agent content to Windsurf format (plain markdown, no YAML frontmatter)
105
+ */
106
+ export function transformAgent(srcPath, content, opts) {
107
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
108
+ if (!fmMatch) return content;
109
+
110
+ const [, frontmatter, body] = fmMatch;
111
+
112
+ // Extract metadata
113
+ const name = frontmatter.match(/name:\s*(.+)/)?.[1]?.trim();
114
+ const description = frontmatter.match(/description:\s*(.+)/)?.[1]?.trim();
115
+ const toolsMatch = frontmatter.match(/tools:\s*(.+)/)?.[1]?.trim();
116
+ const modelMatch = frontmatter.match(/model:\s*(.+)/)?.[1]?.trim();
117
+
118
+ // Build Windsurf-compatible format (plain markdown, no YAML)
119
+ const lines = [];
120
+ lines.push(`### ${name}`);
121
+ lines.push('');
122
+ if (description) {
123
+ lines.push(`> ${description}`);
124
+ lines.push('');
125
+ }
126
+
127
+ // Parse and include tools as capabilities
128
+ if (toolsMatch) {
129
+ let tools;
130
+ try {
131
+ tools = toolsMatch.startsWith('[')
132
+ ? JSON.parse(toolsMatch)
133
+ : toolsMatch.split(/[,\s]+/).filter(Boolean);
134
+ } catch (e) {
135
+ tools = toolsMatch.split(/[,\s]+/).filter(Boolean);
136
+ }
137
+
138
+ if (tools.length > 0) {
139
+ lines.push('<capabilities>');
140
+ tools.forEach(t => lines.push(`- ${t.trim()}`));
141
+ lines.push('</capabilities>');
142
+ lines.push('');
143
+ }
144
+ }
145
+
146
+ if (modelMatch) {
147
+ lines.push(`**Model**: ${modelMatch}`);
148
+ lines.push('');
149
+ }
150
+
151
+ lines.push(body.trim());
152
+ return lines.join('\n');
153
+ }
154
+
155
+ /**
156
+ * Transform command content to Windsurf workflow format
157
+ */
158
+ export function transformCommand(srcPath, content, opts) {
159
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
160
+ if (!fmMatch) return content;
161
+
162
+ const [, frontmatter, body] = fmMatch;
163
+
164
+ // Extract name and description from frontmatter
165
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
166
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
167
+
168
+ const name = nameMatch ? nameMatch[1].trim() : 'Workflow';
169
+ const description = descMatch ? descMatch[1].trim() : '';
170
+
171
+ // Build workflow format
172
+ const lines = [];
173
+ lines.push(`# ${name}`);
174
+ lines.push('');
175
+ if (description) {
176
+ lines.push(`> ${description}`);
177
+ lines.push('');
178
+ }
179
+ lines.push('## Instructions');
180
+ lines.push('');
181
+ lines.push(body.trim());
182
+
183
+ return lines.join('\n');
184
+ }
185
+
186
+ /**
187
+ * Transform rule content for Windsurf — injects trigger frontmatter per ADR-2.
188
+ *
189
+ * Defaults to `trigger: always_on` (the ADR-2 §2 default-preservation choice
190
+ * for the existing AIWG rule corpus). When the source frontmatter declares
191
+ * a `globs:` or `applyTo:` field, the trigger is upgraded to `glob` and the
192
+ * glob pattern is passed through as the Windsurf-native `glob:` field
193
+ * (PUW-020 / #1121, glob-mode part of ADR-2 §1 mapping).
194
+ *
195
+ * Per ADR-2 §5: non-always_on modes require a live-Windsurf smoke test
196
+ * gate before shipping to operators with the live loader. Until that gate
197
+ * lands, the safe default is preserved for rules without explicit globs.
198
+ */
199
+ export function transformRule(srcPath, content, opts) {
200
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
201
+
202
+ if (fmMatch) {
203
+ const [, frontmatter, body] = fmMatch;
204
+ // Already has a trigger field — preserve the file as-is.
205
+ if (/^trigger:/m.test(frontmatter)) {
206
+ return content;
207
+ }
208
+
209
+ // PUW-021 (#1122 sibling): pass through globs / applyTo as glob trigger.
210
+ const globsMatch = /^\s*globs?\s*:\s*(.+)$/m.exec(frontmatter);
211
+ const applyToMatch = /^\s*applyTo\s*:\s*(.+)$/m.exec(frontmatter);
212
+ if (globsMatch || applyToMatch) {
213
+ const globValue = (globsMatch?.[1] || applyToMatch?.[1] || '').trim().replace(/^['"]|['"]$/g, '');
214
+ const triggerLines = [`trigger: glob`];
215
+ // Add Windsurf-native `glob:` field if not already present.
216
+ if (!/^\s*glob\s*:/m.test(frontmatter)) {
217
+ triggerLines.push(`glob: '${globValue}'`);
218
+ }
219
+ return `---\n${triggerLines.join('\n')}\n${frontmatter}\n---\n${body}`;
220
+ }
221
+
222
+ // Default: trigger: always_on (ADR-2 §2 safe default).
223
+ return `---\ntrigger: always_on\n${frontmatter}\n---\n${body}`;
224
+ }
225
+
226
+ // No frontmatter — wrap with trigger.
227
+ return `---\ntrigger: always_on\n---\n\n${content}`;
228
+ }
229
+
230
+ // ============================================================================
231
+ // Model Mapping (not applicable for Windsurf)
232
+ // ============================================================================
233
+
234
+ export function mapModel(shorthand, modelCfg, modelsConfig) {
235
+ return shorthand;
236
+ }
237
+
238
+ // ============================================================================
239
+ // AGENTS.md Generation
240
+ // ============================================================================
241
+
242
+ /**
243
+ * Generate AGENTS.md for Windsurf with all agents aggregated
244
+ */
245
+ export function generateAgentsMd(files, destPath, opts) {
246
+ const { dryRun } = opts;
247
+
248
+ const lines = [];
249
+ lines.push('# AGENTS.md');
250
+ // Canonical AIWG signature in the first 4 lines so the context-pipeline
251
+ // `isOverwriteSafe` check (#1239) recognizes this file as AIWG-managed and
252
+ // overwrites it with the thin-pointer body. Without this, context-pipeline
253
+ // refuses to replace the legacy 2MB+ aggregate writer's output.
254
+ lines.push('<!-- aiwg-managed -->');
255
+ lines.push('');
256
+ lines.push('> AIWG Agent Directory for Windsurf');
257
+ lines.push('');
258
+ lines.push('<!--');
259
+ lines.push(' [EXPERIMENTAL] Generated by AIWG for Windsurf');
260
+ lines.push(' Windsurf reads this file for directory-scoped AI instructions.');
261
+ lines.push(' See: https://docs.windsurf.com/windsurf/cascade/agents-md');
262
+ lines.push('-->');
263
+ lines.push('');
264
+ lines.push('## Table of Contents');
265
+ lines.push('');
266
+
267
+ // Build TOC and collect agents
268
+ const agents = [];
269
+ for (const f of files) {
270
+ const content = fs.readFileSync(f, 'utf8');
271
+ const nameMatch = content.match(/^name:\s*(.+)$/m);
272
+ const agentName = nameMatch ? nameMatch[1].trim() : path.basename(f, '.md');
273
+ agents.push({ name: agentName, file: f, content });
274
+ const anchor = agentName.replace(/\s+/g, '-').toLowerCase();
275
+ lines.push(`- [${agentName}](#${anchor})`);
276
+ }
277
+ lines.push('');
278
+ lines.push('---');
279
+ lines.push('');
280
+
281
+ // Add each agent
282
+ for (const agent of agents) {
283
+ const transformed = transformAgent(agent.file, agent.content, opts);
284
+ lines.push(transformed);
285
+ lines.push('');
286
+ lines.push('---');
287
+ lines.push('');
288
+ }
289
+
290
+ const output = lines.join('\n');
291
+
292
+ if (dryRun) {
293
+ console.log(`[dry-run] Would write AGENTS.md with ${agents.length} agents`);
294
+ } else {
295
+ fs.writeFileSync(destPath, output, 'utf8');
296
+ console.log(`Created AGENTS.md with ${agents.length} agents at ${path.relative(process.cwd(), destPath)}`);
297
+ }
298
+
299
+ return agents.length;
300
+ }
301
+
302
+ // ============================================================================
303
+ // .windsurfrules Generation
304
+ // ============================================================================
305
+
306
+ /**
307
+ * Generate .windsurf/rules/aiwg-orchestration.md with trigger: always_on frontmatter.
308
+ * Also writes a deprecated .windsurfrules stub for backward compatibility — that file
309
+ * may be silently ignored by Windsurf (not documented in official docs).
310
+ */
311
+ export function generateWindsurfRules(srcRoot, target, opts) {
312
+ const { dryRun } = opts;
313
+
314
+ const lines = [];
315
+ lines.push('---');
316
+ lines.push('trigger: always_on');
317
+ lines.push('---');
318
+ lines.push('');
319
+ lines.push('# AIWG Orchestration for Windsurf');
320
+ lines.push('');
321
+ lines.push('<!--');
322
+ lines.push(' [EXPERIMENTAL] Generated by AIWG for Windsurf');
323
+ lines.push(' This file provides orchestration context for AIWG SDLC workflows.');
324
+ lines.push(' trigger: always_on — included in system prompt on every message.');
325
+ lines.push('-->');
326
+ lines.push('');
327
+
328
+ // Orchestration section
329
+ lines.push('<orchestration>');
330
+ lines.push('## AIWG SDLC Framework');
331
+ lines.push('');
332
+ lines.push('**58 SDLC agents** | **100+ commands** | **49 skills** | **157 templates**');
333
+ lines.push('');
334
+ lines.push('### Natural Language Commands');
335
+ lines.push('');
336
+ lines.push('**Phase Transitions:**');
337
+ lines.push('- "transition to elaboration" | "move to elaboration" | "start elaboration"');
338
+ lines.push('- "ready to deploy" | "begin construction" | "start transition"');
339
+ lines.push('');
340
+ lines.push('**Workflow Requests:**');
341
+ lines.push('- "run iteration {N}" | "start iteration {N}"');
342
+ lines.push('- "deploy to production" | "start deployment"');
343
+ lines.push('');
344
+ lines.push('**Review Cycles:**');
345
+ lines.push('- "security review" | "run security" | "validate security"');
346
+ lines.push('- "run tests" | "execute tests" | "test suite"');
347
+ lines.push('');
348
+ lines.push('**Status Checks:**');
349
+ lines.push('- "where are we" | "what\'s next" | "project status"');
350
+ lines.push('</orchestration>');
351
+ lines.push('');
352
+
353
+ // Key agents section
354
+ lines.push('<agents>');
355
+ lines.push('## Key Agents');
356
+ lines.push('');
357
+ lines.push('For the full catalog of 58+ agents, see @AGENTS.md');
358
+ lines.push('');
359
+ lines.push('### Executive Orchestrator');
360
+ lines.push('**Role**: Coordinate multi-agent workflows and phase transitions.');
361
+ lines.push('**Use**: Phase transitions, complex multi-deliverable workflows.');
362
+ lines.push('');
363
+ lines.push('### Requirements Analyst');
364
+ lines.push('**Role**: Analyze requirements, create use cases and user stories.');
365
+ lines.push('**Use**: "analyze requirements", "create use case for {feature}"');
366
+ lines.push('');
367
+ lines.push('### Architecture Designer');
368
+ lines.push('**Role**: Design system architecture, create ADRs, select technology stacks.');
369
+ lines.push('**Use**: "design architecture", "create SAD", "write ADR for {decision}"');
370
+ lines.push('');
371
+ lines.push('### Security Architect');
372
+ lines.push('**Role**: Lead threat modeling, security requirements, and gates.');
373
+ lines.push('**Use**: "security review", "threat model", "security gate"');
374
+ lines.push('');
375
+ lines.push('### Test Architect');
376
+ lines.push('**Role**: Define test strategy, coverage requirements, automation approach.');
377
+ lines.push('**Use**: "test strategy", "define test plan", "coverage analysis"');
378
+ lines.push('');
379
+ lines.push('### Technical Writer');
380
+ lines.push('**Role**: Create and maintain documentation with voice consistency.');
381
+ lines.push('**Use**: "document {feature}", "update README", "API docs"');
382
+ lines.push('</agents>');
383
+ lines.push('');
384
+
385
+ // Artifacts section
386
+ lines.push('<artifacts>');
387
+ lines.push('## Project Artifacts');
388
+ lines.push('');
389
+ lines.push('All SDLC artifacts stored in `.aiwg/`:');
390
+ lines.push('- `intake/` - Project intake forms');
391
+ lines.push('- `requirements/` - User stories, use cases');
392
+ lines.push('- `architecture/` - SAD, ADRs');
393
+ lines.push('- `testing/` - Test strategy, plans');
394
+ lines.push('- `security/` - Threat models');
395
+ lines.push('- `deployment/` - Deployment plans');
396
+ lines.push('</artifacts>');
397
+ lines.push('');
398
+
399
+ // References section
400
+ lines.push('<references>');
401
+ lines.push('## Full Documentation');
402
+ lines.push('');
403
+ lines.push('- **All Agents**: @AGENTS.md');
404
+ lines.push('- **Templates**: @~/.local/share/ai-writing-guide/agentic/code/frameworks/sdlc-complete/templates/');
405
+ lines.push('- **Commands**: @~/.local/share/ai-writing-guide/agentic/code/frameworks/sdlc-complete/commands/');
406
+ lines.push('- **Repository**: https://github.com/jmagly/aiwg');
407
+ lines.push('</references>');
408
+
409
+ const output = lines.join('\n');
410
+
411
+ // Primary: .windsurf/rules/aiwg-orchestration.md with trigger frontmatter
412
+ const rulesDir = path.join(target, '.windsurf', 'rules');
413
+ const orchestrationPath = path.join(rulesDir, 'aiwg-orchestration.md');
414
+
415
+ if (dryRun) {
416
+ console.log(`[dry-run] Would write .windsurf/rules/aiwg-orchestration.md (trigger: always_on)`);
417
+ } else {
418
+ ensureDir(rulesDir);
419
+ fs.writeFileSync(orchestrationPath, output, 'utf8');
420
+ console.log(`Created .windsurf/rules/aiwg-orchestration.md (trigger: always_on)`);
421
+ }
422
+
423
+ // Deprecated stub: .windsurfrules — retained for backward compat only
424
+ const stubLines = [
425
+ '# AIWG Rules for Windsurf — Deprecated',
426
+ '',
427
+ '> **This file is deprecated.**',
428
+ '> Windsurf reads `.windsurf/rules/*.md` natively (see official docs).',
429
+ '> AIWG orchestration context is now deployed to:',
430
+ '> `.windsurf/rules/aiwg-orchestration.md` (trigger: always_on)',
431
+ '>',
432
+ '> This root-level file may be silently ignored by Windsurf.',
433
+ '> It is retained only for backward compatibility and will be removed in a future release.',
434
+ ];
435
+ const stubPath = path.join(target, '.windsurfrules');
436
+
437
+ if (dryRun) {
438
+ console.log(`[dry-run] Would write .windsurfrules (deprecated stub)`);
439
+ } else {
440
+ fs.writeFileSync(stubPath, stubLines.join('\n'), 'utf8');
441
+ console.log(`Created .windsurfrules (deprecated stub → .windsurf/rules/aiwg-orchestration.md)`);
442
+ }
443
+ }
444
+
445
+ // ============================================================================
446
+ // Workflow Deployment
447
+ // ============================================================================
448
+
449
+ /**
450
+ * Deploy commands as Windsurf workflows
451
+ */
452
+ export function deployWorkflows(commandFiles, targetDir, opts) {
453
+ const { dryRun } = opts;
454
+ const workflowsDir = path.join(targetDir, '.windsurf', 'workflows');
455
+
456
+ if (!dryRun) {
457
+ ensureDir(workflowsDir);
458
+ }
459
+
460
+ console.log(`\nDeploying ${commandFiles.length} commands as Windsurf workflows to ${workflowsDir}...`);
461
+
462
+ for (const cmdFile of commandFiles) {
463
+ const content = fs.readFileSync(cmdFile, 'utf8');
464
+ const workflowContent = transformCommand(cmdFile, content, opts);
465
+
466
+ // Check character limit (12000) - warn if exceeded
467
+ if (workflowContent.length > 12000) {
468
+ console.warn(`Warning: Workflow ${path.basename(cmdFile)} exceeds 12000 char limit (${workflowContent.length} chars)`);
469
+ }
470
+
471
+ const destFile = path.join(workflowsDir, path.basename(cmdFile));
472
+
473
+ if (dryRun) {
474
+ console.log(`[dry-run] deploy workflow: ${path.basename(cmdFile)}`);
475
+ } else {
476
+ fs.writeFileSync(destFile, workflowContent, 'utf8');
477
+ console.log(`deployed workflow: ${path.basename(cmdFile)}`);
478
+ }
479
+ }
480
+ }
481
+
482
+ // ============================================================================
483
+ // Skills Deployment
484
+ // ============================================================================
485
+
486
+ /**
487
+ * Deploy skills to .windsurf/skills/ (primary) and .agents/skills/ (cross-agent compatibility).
488
+ * The .agents/skills/ path is an interop convention for projects using multiple AI coding tools.
489
+ */
490
+ export function deploySkills(skillDirs, targetDir, opts) {
491
+ // Primary: kernel-vs-standard routing (#1212/#1216)
492
+ // - kernel skills → .windsurf/skills/ (platform-native, always-loaded)
493
+ // - standard → .windsurf/.aiwg/skills/ (index-discoverable)
494
+ const standardDestDir = path.join(targetDir, paths.skills);
495
+ const kernelDestDir = path.join(targetDir, kernelSkillsPath);
496
+ deploySkillsWithKernelRouting(skillDirs, standardDestDir, kernelDestDir, opts);
497
+
498
+ // Cross-agent compatibility: .agents/skills/
499
+ const crossAgentDir = path.join(targetDir, paths.crossAgentSkills);
500
+ ensureDir(crossAgentDir, opts.dryRun);
501
+ if (!opts.dryRun) {
502
+ console.log(`Deploying cross-agent skills to ${path.relative(process.cwd(), crossAgentDir)}...`);
503
+ } else {
504
+ console.log(`[dry-run] Would deploy cross-agent skills to .agents/skills/`);
505
+ }
506
+ for (const skillDir of skillDirs) {
507
+ deploySkillDir(skillDir, crossAgentDir, opts);
508
+ }
509
+ }
510
+
511
+ // ============================================================================
512
+ // Rules Deployment
513
+ // ============================================================================
514
+
515
+ /**
516
+ * Deploy rules as discrete files with trigger: always_on frontmatter injected
517
+ */
518
+ export function deployRules(ruleFiles, targetDir, opts) {
519
+ const destDir = path.join(targetDir, paths.rules);
520
+ ensureDir(destDir, opts.dryRun);
521
+ cleanupOldRuleFiles(destDir, opts);
522
+ return deployFiles(ruleFiles, destDir, opts, transformRule);
523
+ }
524
+
525
+ // ============================================================================
526
+ // Post-Deployment
527
+ // ============================================================================
528
+
529
+ export async function postDeploy(targetDir, opts) {
530
+ initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
531
+ }
532
+
533
+ // ============================================================================
534
+ // File Extension
535
+ // ============================================================================
536
+
537
+ export function getFileExtension(type) {
538
+ return '.md';
539
+ }
540
+
541
+ // ============================================================================
542
+ // Main Deploy Function
543
+ // ============================================================================
544
+
545
+ export async function deploy(opts) {
546
+ const {
547
+ srcRoot,
548
+ target,
549
+ mode,
550
+ deployCommands,
551
+ deploySkills: shouldDeploySkills,
552
+ deployRules: shouldDeployRules,
553
+ commandsOnly,
554
+ skillsOnly,
555
+ rulesOnly,
556
+ dryRun
557
+ } = opts;
558
+
559
+ displayWarning();
560
+
561
+ console.log(`\n=== Windsurf Provider (EXPERIMENTAL) ===`);
562
+ console.log(`Target: ${target}`);
563
+ console.log(`Mode: ${mode}`);
564
+ const normalizedMode = normalizeDeploymentMode(mode);
565
+
566
+ // Collect all agent files based on mode
567
+ const allAgentFiles = [];
568
+
569
+ // All addons (dynamically discovered)
570
+ if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
571
+ allAgentFiles.push(...getAddonAgentFiles(srcRoot));
572
+ }
573
+
574
+ const frameworkAgents = collectFrameworkArtifacts(srcRoot, normalizedMode, {
575
+ includeAgents: true,
576
+ includeCommands: false,
577
+ includeSkills: false,
578
+ includeRules: false
579
+ });
580
+ allAgentFiles.push(...frameworkAgents.agents);
581
+ const soulFiles = [...(frameworkAgents.souls || [])];
582
+
583
+ // Generate aggregated AGENTS.md
584
+ if (allAgentFiles.length > 0 && !commandsOnly && !skillsOnly && !rulesOnly) {
585
+ const agentsMdPath = path.join(target, 'AGENTS.md');
586
+ console.log(`\nGenerating AGENTS.md with ${allAgentFiles.length} agents...`);
587
+ generateAgentsMd(allAgentFiles, agentsMdPath, opts);
588
+
589
+ // Deploy soul companion files alongside agents (discrete mirror dir)
590
+ if (soulFiles.length > 0) {
591
+ const destDir = path.join(target, paths.agents);
592
+ ensureDir(destDir, opts.dryRun);
593
+ console.log(`\nDeploying ${soulFiles.length} soul files...`);
594
+ deploySoulCompanions(soulFiles, destDir, opts);
595
+ }
596
+ }
597
+
598
+ // Generate .windsurf/rules/aiwg-orchestration.md (trigger: always_on) + deprecated .windsurfrules stub
599
+ if (!commandsOnly && !skillsOnly && !rulesOnly) {
600
+ console.log('\nGenerating orchestration rule (.windsurf/rules/aiwg-orchestration.md)...');
601
+ generateWindsurfRules(srcRoot, target, opts);
602
+ }
603
+
604
+ // Collect skill directories early so we can filter command collisions
605
+ const skillDirs = [];
606
+ if (shouldDeploySkills || skillsOnly) {
607
+ // All addons (dynamically discovered)
608
+ if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
609
+ skillDirs.push(...getAddonSkillDirs(srcRoot));
610
+
611
+ // Holistic post-deploy cleanup of stale AIWG-managed kernel
612
+ // skills (renamed/removed sources). Uses the global kernel set
613
+ // (computeAllKernelNames walks all source frameworks/addons),
614
+ // not just this-call's skillDirs, because aiwg use invokes
615
+ // deploy-agents.mjs multiple times.
616
+ {
617
+ const _kernelDestDir = path.isAbsolute(kernelSkillsPath)
618
+ ? kernelSkillsPath
619
+ : path.join(target, kernelSkillsPath);
620
+ pruneStaleAiwgSkills(_kernelDestDir, computeAllKernelNames(srcRoot), opts);
621
+ }
622
+ }
623
+
624
+ const frameworkSkills = collectFrameworkArtifacts(srcRoot, normalizedMode, {
625
+ includeAgents: false,
626
+ includeCommands: false,
627
+ includeSkills: true,
628
+ includeRules: false
629
+ });
630
+ skillDirs.push(...frameworkSkills.skills);
631
+ }
632
+
633
+ // Deploy commands as Windsurf workflows
634
+ if (deployCommands || commandsOnly) {
635
+ // Collect command files based on mode
636
+ const commandFiles = [];
637
+
638
+ // All addons (dynamically discovered)
639
+ if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
640
+ commandFiles.push(...getAddonCommandFiles(srcRoot));
641
+ }
642
+
643
+ const frameworkCommands = collectFrameworkArtifacts(srcRoot, normalizedMode, {
644
+ includeAgents: false,
645
+ includeCommands: true,
646
+ includeSkills: false,
647
+ includeRules: false,
648
+ recursiveCommands: true
649
+ });
650
+ commandFiles.push(...frameworkCommands.commands);
651
+
652
+ // Filter commands that collide with skills (skills take precedence)
653
+ const filteredCommands = skillDirs.length > 0
654
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
655
+ : commandFiles;
656
+
657
+ if (filteredCommands.length > 0) {
658
+ deployWorkflows(filteredCommands, target, opts);
659
+ }
660
+ }
661
+
662
+ // Deploy skills
663
+ if (skillDirs.length > 0 && (shouldDeploySkills || skillsOnly)) {
664
+ console.log(`\nDeploying ${skillDirs.length} skills...`);
665
+ deploySkills(skillDirs, target, opts);
666
+ }
667
+
668
+ // Deploy rules
669
+ if (shouldDeployRules || rulesOnly) {
670
+ // Collect rule files based on mode
671
+ const ruleFiles = [];
672
+
673
+ // All addons (dynamically discovered)
674
+ if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
675
+ ruleFiles.push(...getAddonRuleFiles(srcRoot));
676
+ }
677
+
678
+ const frameworkRules = collectFrameworkArtifacts(srcRoot, normalizedMode, {
679
+ includeAgents: false,
680
+ includeCommands: false,
681
+ includeSkills: false,
682
+ includeRules: true,
683
+ consolidatedSdlcRules: true
684
+ });
685
+ ruleFiles.push(...frameworkRules.rules);
686
+
687
+ if (ruleFiles.length > 0) {
688
+ console.log(`\nDeploying ${ruleFiles.length} rules...`);
689
+ deployRules(ruleFiles, target, opts);
690
+
691
+ // On-demand index (#1675): list the MEDIUM/LOW rules tier-gated out of the
692
+ // always-on set so agents can fetch them via `aiwg show rule`.
693
+ const onDemandCount = writeOnDemandRuleIndex(
694
+ path.join(target, paths.rules),
695
+ listOnDemandRuleFiles(srcRoot),
696
+ opts,
697
+ );
698
+ if (onDemandCount > 0) {
699
+ console.log(` On-demand rules (not inlined): ${onDemandCount} → RULES-ONDEMAND.md`);
700
+ }
701
+ }
702
+ }
703
+
704
+ // Post-deployment
705
+ await postDeploy(target, opts);
706
+
707
+ console.log('\n' + '='.repeat(70));
708
+ console.log('Windsurf deployment complete. Generated files:');
709
+ console.log(' - AGENTS.md (agent catalog)');
710
+ console.log(' - .windsurf/rules/aiwg-orchestration.md (orchestration context, trigger: always_on)');
711
+ console.log(' - .windsurfrules (deprecated stub — Windsurf may ignore this file)');
712
+ if (deployCommands || commandsOnly) {
713
+ console.log(' - .windsurf/workflows/ (commands as workflows)');
714
+ }
715
+ if (shouldDeploySkills || skillsOnly) {
716
+ console.log(' - .windsurf/skills/ (discrete skill directories)');
717
+ console.log(' - .agents/skills/ (cross-agent compatibility)');
718
+ }
719
+ if (shouldDeployRules || rulesOnly) {
720
+ console.log(' - .windsurf/rules/ (discrete rule files with trigger frontmatter)');
721
+ }
722
+ console.log('='.repeat(70) + '\n');
723
+ }
724
+
725
+ // ============================================================================
726
+ // Default Export
727
+ // ============================================================================
728
+
729
+ export default {
730
+ name,
731
+ aliases,
732
+ paths,
733
+ kernelSkillsPath,
734
+ support,
735
+ capabilities,
736
+ transformAgent,
737
+ transformCommand,
738
+ transformRule,
739
+ mapModel,
740
+ generateAgentsMd,
741
+ generateWindsurfRules,
742
+ deployWorkflows,
743
+ deploySkills,
744
+ deployRules,
745
+ postDeploy,
746
+ getFileExtension,
747
+ deploy
748
+ };