aiwg 2026.2.12 → 2026.2.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiwg",
3
- "version": "2026.2.12",
3
+ "version": "2026.2.13",
4
4
  "description": "Cognitive architecture for AI-augmented software development with structured memory, ensemble validation, and closed-loop correction. FAIR-aligned artifacts, 84% cost reduction via human-in-the-loop, standards adopted by 100+ organizations.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -258,6 +258,38 @@ export function parseTools(toolsString) {
258
258
  return toolsString.split(/[,\s]+/).filter(Boolean);
259
259
  }
260
260
 
261
+ // ============================================================================
262
+ // Skill-Command Collision Detection
263
+ // ============================================================================
264
+
265
+ /**
266
+ * Filter out commands that share a name with a skill.
267
+ * Skills are the richer format (triggers, NL routing, behavior spec) and take precedence.
268
+ *
269
+ * @param {string[]} commandFiles - Array of command file paths
270
+ * @param {string[]} skillDirs - Array of skill directory paths
271
+ * @returns {string[]} Filtered command files with collisions removed
272
+ */
273
+ export function filterCommandsAgainstSkills(commandFiles, skillDirs) {
274
+ if (!skillDirs.length || !commandFiles.length) return commandFiles;
275
+
276
+ // Build set of skill names (directory basenames, without extension)
277
+ const skillNames = new Set(skillDirs.map(d => path.basename(d)));
278
+
279
+ const filtered = [];
280
+ for (const f of commandFiles) {
281
+ // Command name is the filename without extension
282
+ const commandName = path.basename(f).replace(/\.\w+$/, '');
283
+ if (skillNames.has(commandName)) {
284
+ console.log(`skip (skill precedence): command "${commandName}" — skill with same name takes precedence`);
285
+ } else {
286
+ filtered.push(f);
287
+ }
288
+ }
289
+
290
+ return filtered;
291
+ }
292
+
261
293
  // ============================================================================
262
294
  // File Deployment
263
295
  // ============================================================================
@@ -30,7 +30,8 @@ import {
30
30
  getAddonRuleFiles,
31
31
  normalizeDeploymentMode,
32
32
  collectFrameworkArtifacts,
33
- cleanupOldRuleFiles
33
+ cleanupOldRuleFiles,
34
+ filterCommandsAgainstSkills
34
35
  } from './base.mjs';
35
36
 
36
37
  // ============================================================================
@@ -353,9 +354,14 @@ export async function deploy(opts) {
353
354
  deployAgents(filteredAgents, target, opts);
354
355
  }
355
356
 
357
+ // Filter commands that collide with skills (skills take precedence)
358
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
359
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
360
+ : commandFiles;
361
+
356
362
  if (shouldDeployCommands || commandsOnly) {
357
- console.log(`\nDeploying ${commandFiles.length} commands...`);
358
- deployCommands(commandFiles, target, opts);
363
+ console.log(`\nDeploying ${filteredCommands.length} commands...`);
364
+ deployCommands(filteredCommands, target, opts);
359
365
  }
360
366
 
361
367
  if (shouldDeploySkills || skillsOnly) {
@@ -37,7 +37,8 @@ import {
37
37
  getFrameworksForMode,
38
38
  normalizeDeploymentMode,
39
39
  getRulesIndexPath,
40
- cleanupOldRuleFiles
40
+ cleanupOldRuleFiles,
41
+ filterCommandsAgainstSkills
41
42
  } from './base.mjs';
42
43
 
43
44
  // ============================================================================
@@ -35,7 +35,8 @@ import {
35
35
  deploySkillDir,
36
36
  normalizeDeploymentMode,
37
37
  collectFrameworkArtifacts,
38
- cleanupOldRuleFiles
38
+ cleanupOldRuleFiles,
39
+ filterCommandsAgainstSkills
39
40
  } from './base.mjs';
40
41
 
41
42
  // ============================================================================
@@ -430,9 +431,14 @@ export async function deploy(opts) {
430
431
  deployAgents(agentFiles, target, opts);
431
432
  }
432
433
 
434
+ // Filter commands that collide with skills (skills take precedence)
435
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
436
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
437
+ : commandFiles;
438
+
433
439
  if (shouldDeployCommands || commandsOnly) {
434
- console.log(`\nDeploying ${commandFiles.length} commands as agents (YAML format)...`);
435
- deployCommands(commandFiles, target, opts);
440
+ console.log(`\nDeploying ${filteredCommands.length} commands as agents (YAML format)...`);
441
+ deployCommands(filteredCommands, target, opts);
436
442
  }
437
443
 
438
444
  if (shouldDeploySkills || skillsOnly) {
@@ -37,7 +37,8 @@ import {
37
37
  initializeFrameworkWorkspace,
38
38
  normalizeDeploymentMode,
39
39
  collectFrameworkArtifacts,
40
- cleanupOldRuleFiles
40
+ cleanupOldRuleFiles,
41
+ filterCommandsAgainstSkills
41
42
  } from './base.mjs';
42
43
 
43
44
  // ============================================================================
@@ -363,9 +364,14 @@ export async function deploy(opts) {
363
364
  deployAgents(filteredAgents, target, opts);
364
365
  }
365
366
 
367
+ // Filter commands that collide with skills (skills take precedence)
368
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
369
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
370
+ : commandFiles;
371
+
366
372
  if (shouldDeployCommands || commandsOnly) {
367
- console.log(`\nDeploying ${commandFiles.length} commands...`);
368
- deployCommands(commandFiles, target, opts);
373
+ console.log(`\nDeploying ${filteredCommands.length} commands...`);
374
+ deployCommands(filteredCommands, target, opts);
369
375
  }
370
376
 
371
377
  if (shouldDeploySkills || skillsOnly) {
@@ -37,7 +37,8 @@ import {
37
37
  deploySkillDir,
38
38
  normalizeDeploymentMode,
39
39
  collectFrameworkArtifacts,
40
- cleanupOldRuleFiles
40
+ cleanupOldRuleFiles,
41
+ filterCommandsAgainstSkills
41
42
  } from './base.mjs';
42
43
 
43
44
  // ============================================================================
@@ -537,9 +538,14 @@ export async function deploy(opts) {
537
538
  deployAgents(agentFiles, target, opts);
538
539
  }
539
540
 
541
+ // Filter commands that collide with skills (skills take precedence)
542
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
543
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
544
+ : commandFiles;
545
+
540
546
  if (shouldDeployCommands || commandsOnly) {
541
- console.log(`\nDeploying ${commandFiles.length} commands...`);
542
- deployCommands(commandFiles, target, opts);
547
+ console.log(`\nDeploying ${filteredCommands.length} commands...`);
548
+ deployCommands(filteredCommands, target, opts);
543
549
  }
544
550
 
545
551
  if (shouldDeploySkills || skillsOnly) {
@@ -34,7 +34,8 @@ import {
34
34
  deploySkillDir,
35
35
  normalizeDeploymentMode,
36
36
  collectFrameworkArtifacts,
37
- cleanupOldRuleFiles
37
+ cleanupOldRuleFiles,
38
+ filterCommandsAgainstSkills
38
39
  } from './base.mjs';
39
40
 
40
41
  // ============================================================================
@@ -399,9 +400,14 @@ export async function deploy(opts) {
399
400
  deployAgents(agentFiles, target, opts);
400
401
  }
401
402
 
403
+ // Filter commands that collide with skills (skills take precedence)
404
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
405
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
406
+ : commandFiles;
407
+
402
408
  if (shouldDeployCommands || commandsOnly) {
403
- console.log(`\nDeploying ${commandFiles.length} commands...`);
404
- deployCommands(commandFiles, target, opts);
409
+ console.log(`\nDeploying ${filteredCommands.length} commands...`);
410
+ deployCommands(filteredCommands, target, opts);
405
411
  }
406
412
 
407
413
  if (shouldDeploySkills || skillsOnly) {
@@ -38,7 +38,8 @@ import {
38
38
  getAddonRuleFiles,
39
39
  normalizeDeploymentMode,
40
40
  collectFrameworkArtifacts,
41
- cleanupOldRuleFiles
41
+ cleanupOldRuleFiles,
42
+ filterCommandsAgainstSkills
42
43
  } from './base.mjs';
43
44
 
44
45
  // ============================================================================
@@ -365,9 +366,14 @@ export async function deploy(opts) {
365
366
  deployAgents(filteredAgents, target, opts);
366
367
  }
367
368
 
369
+ // Filter commands that collide with skills (skills take precedence)
370
+ const filteredCommands = (shouldDeploySkills || skillsOnly)
371
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
372
+ : commandFiles;
373
+
368
374
  if (shouldDeployCommands || commandsOnly) {
369
- console.log(`\nDeploying ${commandFiles.length} commands...`);
370
- deployCommands(commandFiles, target, opts);
375
+ console.log(`\nDeploying ${filteredCommands.length} commands...`);
376
+ deployCommands(filteredCommands, target, opts);
371
377
  }
372
378
 
373
379
  if (shouldDeploySkills || skillsOnly) {
@@ -35,7 +35,8 @@ import {
35
35
  deployFiles,
36
36
  normalizeDeploymentMode,
37
37
  collectFrameworkArtifacts,
38
- cleanupOldRuleFiles
38
+ cleanupOldRuleFiles,
39
+ filterCommandsAgainstSkills
39
40
  } from './base.mjs';
40
41
 
41
42
  // ============================================================================
@@ -478,6 +479,23 @@ export async function deploy(opts) {
478
479
  generateWindsurfRules(srcRoot, target, opts);
479
480
  }
480
481
 
482
+ // Collect skill directories early so we can filter command collisions
483
+ const skillDirs = [];
484
+ if (shouldDeploySkills || skillsOnly) {
485
+ // All addons (dynamically discovered)
486
+ if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
487
+ skillDirs.push(...getAddonSkillDirs(srcRoot));
488
+ }
489
+
490
+ const frameworkSkills = collectFrameworkArtifacts(srcRoot, normalizedMode, {
491
+ includeAgents: false,
492
+ includeCommands: false,
493
+ includeSkills: true,
494
+ includeRules: false
495
+ });
496
+ skillDirs.push(...frameworkSkills.skills);
497
+ }
498
+
481
499
  // Deploy commands as Windsurf workflows
482
500
  if (deployCommands || commandsOnly) {
483
501
  // Collect command files based on mode
@@ -497,33 +515,20 @@ export async function deploy(opts) {
497
515
  });
498
516
  commandFiles.push(...frameworkCommands.commands);
499
517
 
500
- if (commandFiles.length > 0) {
501
- deployWorkflows(commandFiles, target, opts);
518
+ // Filter commands that collide with skills (skills take precedence)
519
+ const filteredCommands = skillDirs.length > 0
520
+ ? filterCommandsAgainstSkills(commandFiles, skillDirs)
521
+ : commandFiles;
522
+
523
+ if (filteredCommands.length > 0) {
524
+ deployWorkflows(filteredCommands, target, opts);
502
525
  }
503
526
  }
504
527
 
505
528
  // Deploy skills
506
- if (shouldDeploySkills || skillsOnly) {
507
- // Collect skill directories based on mode
508
- const skillDirs = [];
509
-
510
- // All addons (dynamically discovered)
511
- if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
512
- skillDirs.push(...getAddonSkillDirs(srcRoot));
513
- }
514
-
515
- const frameworkSkills = collectFrameworkArtifacts(srcRoot, normalizedMode, {
516
- includeAgents: false,
517
- includeCommands: false,
518
- includeSkills: true,
519
- includeRules: false
520
- });
521
- skillDirs.push(...frameworkSkills.skills);
522
-
523
- if (skillDirs.length > 0) {
524
- console.log(`\nDeploying ${skillDirs.length} skills...`);
525
- deploySkills(skillDirs, target, opts);
526
- }
529
+ if (skillDirs.length > 0 && (shouldDeploySkills || skillsOnly)) {
530
+ console.log(`\nDeploying ${skillDirs.length} skills...`);
531
+ deploySkills(skillDirs, target, opts);
527
532
  }
528
533
 
529
534
  // Deploy rules
@@ -24,7 +24,7 @@
24
24
  import fs from 'fs';
25
25
  import path from 'path';
26
26
  import os from 'os';
27
- import { getFrameworksForMode, normalizeDeploymentMode } from '../agents/providers/base.mjs';
27
+ import { getFrameworksForMode, normalizeDeploymentMode, getAddonSkillDirs, listSkillDirs, collectFrameworkArtifacts } from '../agents/providers/base.mjs';
28
28
 
29
29
  const CODEX_PROMPTS_DIR = path.join(os.homedir(), '.codex', 'prompts');
30
30
 
@@ -283,6 +283,15 @@ function getCommandDirectories(srcRoot, mode) {
283
283
  ensureDir(target);
284
284
  }
285
285
 
286
+ // Collect skill names to detect command/skill collisions
287
+ const skillNames = new Set();
288
+ const addonSkills = getAddonSkillDirs(srcRoot);
289
+ for (const d of addonSkills) skillNames.add(path.basename(d));
290
+ const frameworkSkills = collectFrameworkArtifacts(srcRoot, mode, {
291
+ includeAgents: false, includeCommands: false, includeSkills: true, includeRules: false
292
+ });
293
+ for (const d of frameworkSkills.skills) skillNames.add(path.basename(d));
294
+
286
295
  // Get command directories based on mode
287
296
  const commandDirs = getCommandDirectories(srcRoot, mode);
288
297
  let totalDeployed = 0;
@@ -295,6 +304,13 @@ function getCommandDirectories(srcRoot, mode) {
295
304
  console.log(`\n${label} (${commandFiles.length} prompts):`);
296
305
 
297
306
  for (const commandFile of commandFiles) {
307
+ // Skip commands that collide with skills (skills take precedence)
308
+ const commandName = path.basename(commandFile, '.md');
309
+ if (skillNames.has(commandName)) {
310
+ console.log(` skip (skill precedence): command "${commandName}" — skill with same name takes precedence`);
311
+ totalSkipped++;
312
+ continue;
313
+ }
298
314
  try {
299
315
  const prompt = transformToCodexPrompt(commandFile, prefix);
300
316
  const result = deployPrompt(prompt, target, { force, dryRun });