@aiwg/cli 2026.8.7 → 2026.8.10

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 (76) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  6. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  7. package/dist/src/artifacts/cli.js +2 -2
  8. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  9. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  10. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  11. package/dist/src/artifacts/discover-facets.js +2 -2
  12. package/dist/src/artifacts/embedding-index.js +9 -8
  13. package/dist/src/artifacts/graph-backend.js +2 -2
  14. package/dist/src/artifacts/move.js +40 -2
  15. package/dist/src/artifacts/query-engine.js +21 -7
  16. package/dist/src/artifacts/repair.js +47 -0
  17. package/dist/src/artifacts/types.js +3 -3
  18. package/dist/src/cli/command-log.js +2 -2
  19. package/dist/src/cli/handlers/artifacts.js +50 -1
  20. package/dist/src/cli/handlers/cost-report.js +71 -0
  21. package/dist/src/cli/handlers/evidence.js +78 -0
  22. package/dist/src/cli/handlers/help.js +9 -0
  23. package/dist/src/cli/handlers/index.js +8 -3
  24. package/dist/src/cli/handlers/local-executor.js +4 -3
  25. package/dist/src/cli/handlers/refresh.js +20 -8
  26. package/dist/src/cli/handlers/regenerate.js +22 -12
  27. package/dist/src/cli/handlers/serve.js +15 -36
  28. package/dist/src/cli/handlers/sessions.js +5 -4
  29. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  30. package/dist/src/cli/handlers/subcommands.js +30 -0
  31. package/dist/src/cli/handlers/use.js +272 -89
  32. package/dist/src/cli/handlers/utilities.js +149 -0
  33. package/dist/src/cli/handlers/workspace.js +10 -0
  34. package/dist/src/cli/help-generator.js +2 -1
  35. package/dist/src/cli/regenerate-selector.js +94 -0
  36. package/dist/src/cli/router.js +4 -1
  37. package/dist/src/cli/services/deployment-verification.js +596 -0
  38. package/dist/src/cli/skill-usage.js +2 -2
  39. package/dist/src/cli/workflow-orchestrator.js +1 -1
  40. package/dist/src/cli/workspace-signals.js +2 -2
  41. package/dist/src/config/aiwg-config.js +54 -27
  42. package/dist/src/config/cli.js +3 -3
  43. package/dist/src/config/project-artifacts-health.js +2 -0
  44. package/dist/src/config/project-artifacts-health.mjs +123 -0
  45. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  46. package/dist/src/config/project-artifacts.js +2 -1
  47. package/dist/src/cost/fleet-report.js +329 -0
  48. package/dist/src/evidence/bundle.js +256 -0
  49. package/dist/src/extensions/commands/definitions.js +77 -25
  50. package/dist/src/extensions/deployment-registration.js +6 -4
  51. package/dist/src/extensions/project-local-doctor.js +10 -6
  52. package/dist/src/extensions/project-local-gitignore.js +8 -4
  53. package/dist/src/extensions/project-quickref.js +197 -10
  54. package/dist/src/features/catalog.js +26 -0
  55. package/dist/src/features/cli.js +1 -3
  56. package/dist/src/features/runtime.js +17 -1
  57. package/dist/src/issues/cli.js +91 -7
  58. package/dist/src/mcp/server.mjs +1 -1
  59. package/dist/src/ops/registry.js +2 -2
  60. package/dist/src/policy/authorization.js +2 -2
  61. package/dist/src/providers/capability-matrix.yaml +3 -3
  62. package/dist/src/providers/provider-definitions.js +7 -5
  63. package/dist/src/providers/provider-definitions.mjs +1 -1
  64. package/dist/src/serve/pty-bridge.js +2 -8
  65. package/dist/src/serve/screen-reader.js +3 -6
  66. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  67. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  68. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  69. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  70. package/package.json +2 -1
  71. package/tools/agents/deploy-agents.mjs +10 -11
  72. package/tools/agents/providers/base.mjs +47 -5
  73. package/tools/agents/providers/openclaw.mjs +5 -2
  74. package/tools/agents/providers/windsurf.mjs +13 -24
  75. package/tools/plugin/package-plugins.mjs +53 -0
  76. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -10,7 +10,7 @@ import * as path from 'node:path';
10
10
  import { execFile } from 'node:child_process';
11
11
  import { promisify } from 'node:util';
12
12
  import { readAiwgConfig } from '../../config/aiwg-config.js';
13
- import { projectAiwgPath } from '../../config/project-artifacts.js';
13
+ import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
14
14
  import { renderTrackerProtocol, resolveTrackerAuthority } from '../../tracker/capability-protocol.js';
15
15
  import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
16
16
  export const FINALIZATION_START = '<!-- aiwg-context-finalization:START -->';
@@ -61,7 +61,7 @@ export async function buildContextFinalizationBlock(projectPath) {
61
61
  const installed = Object.entries(config?.installed ?? {});
62
62
  const installedNames = installed.map(([name]) => name);
63
63
  const providerDeployments = new Set();
64
- const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
64
+ const normalizedAiwgMdPath = displayProjectPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
65
65
  const normalizedAiwgMdLabel = `\`${normalizedAiwgMdPath}\``;
66
66
  for (const [, entry] of installed) {
67
67
  for (const provider of Object.keys(entry.deployedTo ?? {})) {
@@ -114,7 +114,7 @@ export function replaceOrAppendFinalizationBlock(content, block) {
114
114
  export async function buildNormalizedAiwgMd(projectPath, existing = '') {
115
115
  const block = await buildContextFinalizationBlock(projectPath);
116
116
  const externalLinksSection = await buildExternalLinksSection(projectPath);
117
- const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
117
+ const normalizedAiwgMdPath = displayProjectPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
118
118
  const base = existing.trim().length > 0
119
119
  ? existing
120
120
  : [
@@ -132,7 +132,7 @@ export async function buildNormalizedAiwgMd(projectPath, existing = '') {
132
132
  return replaceOrAppendExternalLinksBlock(withFinalization, externalLinksSection);
133
133
  }
134
134
  export async function writeNormalizedAiwgMd(projectPath) {
135
- const targetPath = projectAiwgPath(projectPath, 'AIWG.md');
135
+ const targetPath = projectControlPath(projectPath, 'AIWG.md');
136
136
  let existing = '';
137
137
  try {
138
138
  existing = await fs.readFile(targetPath, 'utf8');
@@ -143,7 +143,20 @@ export async function writeNormalizedAiwgMd(projectPath) {
143
143
  }
144
144
  const content = await buildNormalizedAiwgMd(projectPath, existing);
145
145
  await fs.mkdir(path.dirname(targetPath), { recursive: true });
146
- await fs.writeFile(targetPath, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
146
+ const normalizedContent = content.endsWith('\n') ? content : `${content}\n`;
147
+ await fs.writeFile(targetPath, normalizedContent, 'utf8');
148
+ const artifactRoot = resolveProjectAiwgDir(projectPath);
149
+ const artifactPath = projectAiwgPath(projectPath, 'AIWG.md');
150
+ if (artifactPath !== targetPath) {
151
+ try {
152
+ await fs.access(artifactRoot);
153
+ await fs.writeFile(artifactPath, normalizedContent, 'utf8');
154
+ }
155
+ catch (error) {
156
+ if (error.code !== 'ENOENT')
157
+ throw error;
158
+ }
159
+ }
147
160
  return targetPath;
148
161
  }
149
162
  //# sourceMappingURL=finalization.js.map
@@ -21,7 +21,7 @@ import { shouldEmitAgentsMd, shouldEmitAiwgMd, shouldEmitClaudeMdHook } from './
21
21
  import { ensureClaudeMdHook } from './claude-hook.js';
22
22
  import { ensureManagedHook } from './managed-hook.js';
23
23
  import { buildProviderBootstrapBlock, ensureWorkspaceContext, registerProviderContext, } from './workspace-context.js';
24
- import { projectAiwgPath } from '../../config/project-artifacts.js';
24
+ import { projectControlPath } from '../../config/project-artifacts.js';
25
25
  const SECTION_TITLES = {
26
26
  agents: 'Agents',
27
27
  rules: 'Rules',
@@ -137,7 +137,7 @@ export async function buildAgentsMd(opts) {
137
137
  parts.push('');
138
138
  parts.push('See [AIWG.md](./AIWG.md) for the full AIWG framework context');
139
139
  parts.push('(active frameworks, addons, agents, behaviors, rules).');
140
- const configRelative = path.relative(opts.projectPath, projectAiwgPath(opts.projectPath, 'aiwg.config')).replace(/\\/g, '/');
140
+ const configRelative = path.relative(opts.projectPath, projectControlPath(opts.projectPath, 'aiwg.config')).replace(/\\/g, '/');
141
141
  const configLink = configRelative.startsWith('.') ? configRelative : `./${configRelative}`;
142
142
  parts.push(`Tracker and delivery source of truth: [${configRelative}](${configLink}).`);
143
143
  parts.push('');
@@ -12,7 +12,8 @@ import * as path from 'node:path';
12
12
  import { buildAiwgMdContent } from './aiwg-md.js';
13
13
  import { buildNormalizedAiwgMd } from './finalization.js';
14
14
  import { getProviderDefinition, listProviderDefinitions, } from '../../providers/provider-definitions.js';
15
- import { projectAiwgPath, resolveProjectAiwgDir } from '../../config/project-artifacts.js';
15
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
16
+ import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
16
17
  export const WORKSPACE_MANAGED_START = '<!-- AIWG:workspace-context:start -->';
17
18
  export const WORKSPACE_MANAGED_END = '<!-- AIWG:workspace-context:end -->';
18
19
  export const WORKSPACE_OPERATOR_START = '<!-- AIWG:workspace-operator:start -->';
@@ -306,8 +307,8 @@ function providerForPath(relativePath) {
306
307
  }
307
308
  return null;
308
309
  }
309
- function projectArtifactMarkdownPath(projectPath, ...segments) {
310
- const rel = path.relative(projectPath, projectAiwgPath(projectPath, ...segments)).replace(/\\/g, '/');
310
+ function projectControlMarkdownPath(projectPath, ...segments) {
311
+ const rel = path.relative(projectPath, projectControlPath(projectPath, ...segments)).replace(/\\/g, '/');
311
312
  if (rel === '')
312
313
  return '.';
313
314
  return rel.startsWith('.') ? rel : `./${rel}`;
@@ -315,14 +316,15 @@ function projectArtifactMarkdownPath(projectPath, ...segments) {
315
316
  function workspaceLinks(projectPath, providerFiles = []) {
316
317
  const links = new Set([
317
318
  '[AIWG framework context](./AIWG.md)',
318
- `[AIWG project configuration](${projectArtifactMarkdownPath(projectPath, 'aiwg.config')})`,
319
+ `[AIWG project configuration](${projectControlMarkdownPath(projectPath, 'aiwg.config')})`,
319
320
  ]);
320
321
  if (providerFiles.length > 0) {
321
322
  for (const file of providerFiles)
322
323
  links.add(`[Provider-specific context](./${file.replace(/\\/g, '/')})`);
323
324
  }
324
- // The quickref source is linked, not copied into each provider directory.
325
- links.add(`[Project-local quickref](${projectArtifactMarkdownPath(projectPath, 'quickref.json')}) (when configured)`);
325
+ // Keep the logical project path portable in committed context. Runtime
326
+ // quickref readers follow `.aiwg-location` to the external corpus.
327
+ links.add('[Project-local quickref](.aiwg/quickref.json) (when configured)');
326
328
  return [...links];
327
329
  }
328
330
  export function buildWorkspaceManagedBlock(projectPath, providerFiles = []) {
@@ -692,16 +694,13 @@ function providerMigrationContent(source) {
692
694
  ].join('\n');
693
695
  }
694
696
  async function configuredProviders(projectPath) {
695
- const config = await readOptional(projectAiwgPath(projectPath, 'aiwg.config'));
696
- if (config) {
697
- try {
698
- const parsed = JSON.parse(config);
699
- if (Array.isArray(parsed.providers))
700
- return parsed.providers.filter((provider) => typeof provider === 'string');
701
- }
702
- catch {
703
- // Audit/migration reports malformed provider config through the normal CLI path.
704
- }
697
+ try {
698
+ const config = await readAiwgConfig(projectPath);
699
+ if (config)
700
+ return config.providers.filter((provider) => typeof provider === 'string');
701
+ }
702
+ catch {
703
+ // Audit/migration reports malformed provider config through the normal CLI path.
705
704
  }
706
705
  return ['claude'];
707
706
  }
@@ -783,7 +782,7 @@ async function stageMigrationWrites(projectPath, audit, options = {}) {
783
782
  writes.set(configPath, `${JSON.stringify(config, null, 2)}\n`);
784
783
  }
785
784
  if (options.includeGeneratedContext) {
786
- const normalizedPath = projectOutputPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
785
+ const normalizedPath = projectOutputPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
787
786
  const existingNormalized = await readOptional(path.join(projectPath, normalizedPath)) ?? '';
788
787
  writes.set(normalizedPath, await buildNormalizedAiwgMd(projectPath, existingNormalized));
789
788
  const stagedClaude = writes.has('CLAUDE.md')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.8.7",
3
+ "version": "2026.8.10",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "files": [
26
26
  "LICENSE",
27
+ "THIRD_PARTY_NOTICES.md",
27
28
  "bin/",
28
29
  "agentic/code/providers/",
29
30
  "dist/src/",
@@ -20,7 +20,7 @@
20
20
  * --rules-only Deploy only rules (skip agents)
21
21
  * --dry-run Show what would be deployed without writing
22
22
  * --force Overwrite existing files
23
- * --provider <name> Target provider: claude (default), openai, codex, cursor, opencode, copilot, factory, warp, windsurf, hermes, or openclaw
23
+ * --provider <name> Target provider: claude (default), openai, codex, cursor, opencode, copilot, factory, warp, devin, hermes, or openclaw
24
24
  * --model <name> Override model for all tiers (blanket)
25
25
  * --reasoning-model <name> Override model for reasoning tasks
26
26
  * --coding-model <name> Override model for coding tasks
@@ -49,7 +49,8 @@
49
49
  * copilot - GitHub Copilot - .github/agents/, .github/commands/, .github/skills/, .github/copilot-rules/
50
50
  * cursor - Cursor IDE - .cursor/agents/, .cursor/commands/, .cursor/skills/, .cursor/rules/
51
51
  * warp - Warp Terminal - .warp/agents/, .warp/commands/, .warp/skills/, .warp/rules/ + WARP.md
52
- * windsurf - Windsurf - .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
52
+ * devin - Devin Desktop - .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
53
+ * windsurf - Deprecated alias for devin
53
54
  * openclaw - OpenClaw - ~/.openclaw/agents/, ~/.openclaw/commands/, ~/.openclaw/skills/, ~/.openclaw/rules/, ~/.openclaw/behaviors/
54
55
  *
55
56
  * Defaults:
@@ -103,6 +104,7 @@ function getDeployVersion(srcRoot) {
103
104
 
104
105
  const PROVIDER_ALIASES = {
105
106
  'openai': 'codex',
107
+ 'devin': 'windsurf',
106
108
  'devin-desktop': 'windsurf',
107
109
  'devin-local': 'windsurf',
108
110
  'cascade': 'windsurf',
@@ -111,15 +113,9 @@ const PROVIDER_ALIASES = {
111
113
  const AVAILABLE_PROVIDERS = ['claude', 'factory', 'codex', 'opencode', 'copilot', 'cursor', 'warp', 'windsurf', 'hermes', 'openclaw', 'openhuman'];
112
114
 
113
115
  const UNSUPPORTED_PROVIDER_HINTS = {
114
- devin: [
115
- 'Devin Desktop is supported through the Windsurf compatibility adapter:',
116
- ' aiwg use sdlc --provider windsurf',
117
- ' aiwg use sdlc --provider devin-desktop',
118
- 'Devin CLI has distinct rules/skills surfaces and is recorded as future-provider metadata; AIWG does not emit .devin/ provider output yet.',
119
- ],
120
116
  'devin-cli': [
121
117
  'Devin CLI has distinct rules/skills surfaces and is not a deployable AIWG provider yet.',
122
- 'Use --provider windsurf or --provider devin-desktop for Devin Desktop/Windsurf local IDE deployments.',
118
+ 'Use --provider devin for Devin Desktop deployments.',
123
119
  ],
124
120
  };
125
121
 
@@ -531,7 +527,7 @@ Providers (all deploy agents, commands, skills, and rules):
531
527
  Paths: .cursor/agents/, .cursor/commands/, .cursor/skills/, .cursor/rules/
532
528
  warp - Warp Terminal
533
529
  Paths: .warp/agents/, .warp/commands/, .warp/skills/, .warp/rules/ + WARP.md
534
- windsurf - Windsurf
530
+ devin - Devin Desktop (preferred; aliases: devin-desktop, windsurf)
535
531
  Paths: .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
536
532
  hermes - Hermes Agent (MCP-based integration)
537
533
  Skills: ~/.hermes/skills/ (user-global) | Agents: AGENTS.md (lean routing guide)
@@ -865,7 +861,10 @@ export async function main() {
865
861
  srcRoot,
866
862
  target: cfg.target,
867
863
  mode: cfg.mode,
868
- provider: cfg.provider,
864
+ // Provider aliases are selectors, not artifact identities. Passing the
865
+ // canonical id keeps generated frontmatter byte-identical for devin,
866
+ // devin-desktop, and the deprecated windsurf selector.
867
+ provider: resolveProvider(cfg.provider),
869
868
  dryRun: cfg.dryRun,
870
869
  force: cfg.force,
871
870
  reasoningModel: cfg.reasoningModel,
@@ -44,6 +44,48 @@ export function ensureDir(d, dryRun = false) {
44
44
  if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
45
45
  }
46
46
 
47
+ const PROJECT_ARTIFACT_ENV_KEYS = [
48
+ 'AIWG_ARTIFACTS_PATH',
49
+ 'AIWG_PROJECT_ARTIFACTS_PATH',
50
+ 'AIWG_PROJECT_AIWG_DIR',
51
+ ];
52
+
53
+ function expandProjectArtifactPath(value, projectDir) {
54
+ const trimmed = value.trim();
55
+ if (trimmed === '~') return os.homedir();
56
+ if (trimmed.startsWith('~/')) return path.resolve(os.homedir(), trimmed.slice(2));
57
+ return path.isAbsolute(trimmed) ? trimmed : path.resolve(projectDir, trimmed);
58
+ }
59
+
60
+ function parseProjectArtifactLocation(contents) {
61
+ for (const rawLine of contents.split(/\r?\n/)) {
62
+ let line = rawLine.trim();
63
+ if (!line || line.startsWith('#')) continue;
64
+ if (line.startsWith('export ')) line = line.slice('export '.length).trim();
65
+ const assignment = line.match(/^AIWG_ARTIFACTS_PATH\s*=\s*(.+)$/);
66
+ if (assignment) line = assignment[1].trim();
67
+ if ((line.startsWith('"') && line.endsWith('"')) || (line.startsWith("'") && line.endsWith("'"))) {
68
+ line = line.slice(1, -1);
69
+ }
70
+ return line || null;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /** Resolve the artifact corpus root used for framework-generated workspace data. */
76
+ export function resolveFrameworkWorkspaceRoot(projectDir, env = process.env) {
77
+ for (const key of PROJECT_ARTIFACT_ENV_KEYS) {
78
+ const value = env[key];
79
+ if (typeof value === 'string' && value.trim()) return expandProjectArtifactPath(value, projectDir);
80
+ }
81
+ const pointer = path.join(projectDir, '.aiwg-location');
82
+ if (fs.existsSync(pointer)) {
83
+ const configured = parseProjectArtifactLocation(fs.readFileSync(pointer, 'utf8'));
84
+ if (configured) return expandProjectArtifactPath(configured, projectDir);
85
+ }
86
+ return path.join(projectDir, '.aiwg');
87
+ }
88
+
47
89
  /**
48
90
  * List markdown files in a directory (non-recursive)
49
91
  */
@@ -1441,7 +1483,7 @@ export function deploySkillDir(skillDir, destDir, opts) {
1441
1483
  * Creates .aiwg/frameworks/{framework-id}/ directories
1442
1484
  */
1443
1485
  export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = null) {
1444
- const aiwgBase = path.join(target, '.aiwg');
1486
+ const aiwgBase = resolveFrameworkWorkspaceRoot(target);
1445
1487
  const frameworksDir = path.join(aiwgBase, 'frameworks');
1446
1488
  const sharedDir = path.join(aiwgBase, 'shared');
1447
1489
 
@@ -1498,7 +1540,7 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
1498
1540
  }
1499
1541
  for (const entry of fw.memoryCreates || []) {
1500
1542
  if (entry && typeof entry.path === 'string') {
1501
- console.log(`[dry-run] ${path.join(target, entry.path)}${entry.path.endsWith('/') ? '/' : ''}`);
1543
+ console.log(`[dry-run] ${path.join(aiwgBase, entry.path.slice('.aiwg/'.length))}${entry.path.endsWith('/') ? '/' : ''}`);
1502
1544
  }
1503
1545
  }
1504
1546
  }
@@ -1515,7 +1557,7 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
1515
1557
  for (const subdir of fw.subdirs) {
1516
1558
  ensureDir(path.join(fwBase, subdir));
1517
1559
  }
1518
- initializeMemoryCreates(target, fw.path, fw.memoryCreates);
1560
+ initializeMemoryCreates(aiwgBase, fw.path, fw.memoryCreates);
1519
1561
  }
1520
1562
 
1521
1563
  // Initialize registry.json if it doesn't exist
@@ -1536,12 +1578,12 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
1536
1578
  }
1537
1579
 
1538
1580
 
1539
- function initializeMemoryCreates(target, frameworkPath, creates) {
1581
+ function initializeMemoryCreates(aiwgBase, frameworkPath, creates) {
1540
1582
  for (const entry of creates || []) {
1541
1583
  if (!entry || typeof entry.path !== 'string') continue;
1542
1584
  if (!entry.path.startsWith('.aiwg/')) continue;
1543
1585
 
1544
- const targetPath = path.join(target, entry.path);
1586
+ const targetPath = path.join(aiwgBase, entry.path.slice('.aiwg/'.length));
1545
1587
  const isDirectory = entry.path.endsWith('/') || path.extname(entry.path) === '';
1546
1588
  if (isDirectory) {
1547
1589
  ensureDir(targetPath);
@@ -209,10 +209,13 @@ function deploySkills(skillDirs, opts) {
209
209
  ? skillDirs
210
210
  : skillDirs.filter(d => isKernelSkill(d));
211
211
  if (crossAgentSkills.length > 0) {
212
- const crossAgentDir = path.join(process.cwd(), '.agents', 'skills');
212
+ // Honor the deployment target selected by `aiwg use --target`. Provider
213
+ // subprocesses execute from AIWG_ROOT so process.cwd() would otherwise
214
+ // mutate the framework checkout during isolated/project-targeted deploys.
215
+ const crossAgentDir = path.join(opts.target || process.cwd(), '.agents', 'skills');
213
216
  ensureDir(crossAgentDir, opts.dryRun);
214
217
  if (!opts.dryRun) {
215
- console.log(`Deploying cross-agent skills to ${path.relative(process.cwd(), crossAgentDir)}...`);
218
+ console.log(`Deploying cross-agent skills to ${path.relative(opts.target || process.cwd(), crossAgentDir)}...`);
216
219
  } else {
217
220
  console.log(`[dry-run] Would deploy cross-agent skills to .agents/skills/`);
218
221
  }
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Windsurf Provider (EXPERIMENTAL)
2
+ * Devin Desktop Provider (Windsurf compatibility adapter)
3
3
  *
4
- * Deploys agents to Windsurf format with aggregated AGENTS.md and trigger-frontmatter rules.
4
+ * Deploys agents to the Devin Desktop/Windsurf compatibility surface with
5
+ * aggregated AGENTS.md and trigger-frontmatter rules. The provider id and
6
+ * generated `.windsurf/` paths remain stable for backward compatibility.
5
7
  *
6
8
  * Deployment paths:
7
9
  * - Output: AGENTS.md (aggregated agents)
@@ -18,7 +20,7 @@
18
20
  * - Capabilities tags for tools
19
21
  * - Workflow format for commands
20
22
  * - Conventional skills and rules deployment
21
- * - EXPERIMENTAL: Untested, may require adjustments
23
+ * - Devin Desktop selectors resolve to the stable `windsurf` provider id
22
24
  */
23
25
 
24
26
  import realFs from 'fs';
@@ -57,7 +59,7 @@ import {
57
59
  // ============================================================================
58
60
 
59
61
  export const name = 'windsurf';
60
- export const aliases = [];
62
+ export const aliases = ['devin', 'devin-desktop', 'devin-local', 'cascade'];
61
63
 
62
64
  export const paths = {
63
65
  agents: '.windsurf/agents/', // Discrete mirrors alongside AGENTS.md
@@ -85,17 +87,6 @@ export const capabilities = {
85
87
  yamlFormat: false
86
88
  };
87
89
 
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
90
  // ============================================================================
100
91
  // Content Transformation
101
92
  // ============================================================================
@@ -253,12 +244,12 @@ export function generateAgentsMd(files, destPath, opts) {
253
244
  // refuses to replace the legacy 2MB+ aggregate writer's output.
254
245
  lines.push('<!-- aiwg-managed -->');
255
246
  lines.push('');
256
- lines.push('> AIWG Agent Directory for Windsurf');
247
+ lines.push('> AIWG Agent Directory for Devin Desktop');
257
248
  lines.push('');
258
249
  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');
250
+ lines.push(' Generated by AIWG for Devin Desktop using the Windsurf compatibility adapter.');
251
+ lines.push(' Devin Desktop reads this file for directory-scoped AI instructions.');
252
+ lines.push(' See: https://docs.devin.ai/onboard-devin/agents-md');
262
253
  lines.push('-->');
263
254
  lines.push('');
264
255
  lines.push('## Table of Contents');
@@ -316,10 +307,10 @@ export function generateWindsurfRules(srcRoot, target, opts) {
316
307
  lines.push('trigger: always_on');
317
308
  lines.push('---');
318
309
  lines.push('');
319
- lines.push('# AIWG Orchestration for Windsurf');
310
+ lines.push('# AIWG Orchestration for Devin Desktop');
320
311
  lines.push('');
321
312
  lines.push('<!--');
322
- lines.push(' [EXPERIMENTAL] Generated by AIWG for Windsurf');
313
+ lines.push(' Generated by AIWG for Devin Desktop using the Windsurf compatibility adapter.');
323
314
  lines.push(' This file provides orchestration context for AIWG SDLC workflows.');
324
315
  lines.push(' trigger: always_on — included in system prompt on every message.');
325
316
  lines.push('-->');
@@ -556,9 +547,7 @@ export async function deploy(opts) {
556
547
  dryRun
557
548
  } = opts;
558
549
 
559
- displayWarning();
560
-
561
- console.log(`\n=== Windsurf Provider (EXPERIMENTAL) ===`);
550
+ console.log(`\n=== Devin Desktop Provider (windsurf compatibility adapter) ===`);
562
551
  console.log(`Target: ${target}`);
563
552
  console.log(`Mode: ${mode}`);
564
553
  const normalizedMode = normalizeDeploymentMode(mode);
@@ -657,6 +657,13 @@ for (const [id, sourceRoot] of MANIFEST_PLUGIN_SOURCES) {
657
657
  keywords: manifest.keywords || manifest.tags || [],
658
658
  category: manifest.category || 'productivity',
659
659
  sourceRoot,
660
+ declaredSkills: manifest.skills ?? [],
661
+ // Plugin payloads run outside the checkout. Rewrite references back to
662
+ // their packaged root, including the pre-rename Ralph path retained by
663
+ // older Agent Loop documentation.
664
+ selfReferenceRoots: id === 'agent-loop'
665
+ ? [sourceRoot, 'agentic/code/addons/ralph']
666
+ : [sourceRoot],
660
667
  };
661
668
  }
662
669
 
@@ -774,6 +781,48 @@ function copyDir(src, dest, dryRun = false, filter = null) {
774
781
  return copied;
775
782
  }
776
783
 
784
+ function rewritePackagedSelfReferences(pluginDir, roots = []) {
785
+ if (roots.length === 0) return;
786
+ const pending = [pluginDir];
787
+ while (pending.length > 0) {
788
+ const current = pending.pop();
789
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
790
+ const target = path.join(current, entry.name);
791
+ if (entry.isDirectory()) {
792
+ pending.push(target);
793
+ } else if (entry.isFile() && /\.(?:md|json|ya?ml)$/.test(entry.name)) {
794
+ const original = fs.readFileSync(target, 'utf8');
795
+ let rewritten = original;
796
+ for (const root of roots) {
797
+ rewritten = rewritten.replaceAll(`${root}/`, '${CLAUDE_PLUGIN_ROOT}/');
798
+ }
799
+ if (rewritten !== original) fs.writeFileSync(target, rewritten, 'utf8');
800
+ }
801
+ }
802
+ }
803
+ }
804
+
805
+ function pruneUndeclaredPackagedSkills(pluginDir, declaredSkills) {
806
+ if (!declaredSkills) return;
807
+ const skillsRoot = path.join(pluginDir, 'skills');
808
+ if (!fs.existsSync(skillsRoot)) return;
809
+ const allowed = new Set(declaredSkills);
810
+ for (const entry of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
811
+ if (entry.isDirectory() && !allowed.has(entry.name)) {
812
+ fs.rmSync(path.join(skillsRoot, entry.name), { recursive: true, force: true });
813
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
814
+ const skillId = path.basename(entry.name, '.md');
815
+ if (allowed.has(skillId)) {
816
+ const skillDir = path.join(skillsRoot, skillId);
817
+ fs.mkdirSync(skillDir, { recursive: true });
818
+ fs.renameSync(path.join(skillsRoot, entry.name), path.join(skillDir, 'SKILL.md'));
819
+ } else {
820
+ fs.rmSync(path.join(skillsRoot, entry.name), { force: true });
821
+ }
822
+ }
823
+ }
824
+ }
825
+
777
826
  // Clean plugin directory (except .claude-plugin)
778
827
  function cleanPlugin(pluginDir) {
779
828
  if (!fs.existsSync(pluginDir)) return;
@@ -811,6 +860,10 @@ function packagePlugin(name, config, options) {
811
860
  console.log(` 📁 Copying self-contained source from ${config.sourceRoot}...`);
812
861
  const count = copyDir(config.sourceRoot, pluginDir, options.dryRun);
813
862
  console.log(` ${count} files`);
863
+ if (!options.dryRun) {
864
+ rewritePackagedSelfReferences(pluginDir, config.selfReferenceRoots);
865
+ pruneUndeclaredPackagedSkills(pluginDir, config.declaredSkills);
866
+ }
814
867
  }
815
868
 
816
869
  // Copy sources
@@ -417,6 +417,17 @@ function isFullAiwgSourceRoot(srcRoot) {
417
417
  const repoRoot = path.resolve(scriptDir, '..', '..');
418
418
  const srcRoot = source || repoRoot;
419
419
  const fullAiwgSourceRoot = isFullAiwgSourceRoot(srcRoot);
420
+ const copyStandardSkills = cfg.copyStandardSkills === true;
421
+ // Component-scoped deploys (the way `aiwg use all` installs selected
422
+ // frameworks/addons) still need a complete inventory for stale-skill
423
+ // reconciliation. AIWG_ROOT is supplied by the orchestrator for exactly
424
+ // this purpose; fall back to the script checkout for standalone use.
425
+ const configuredAiwgRoot = process.env.AIWG_ROOT
426
+ ? path.resolve(process.env.AIWG_ROOT)
427
+ : repoRoot;
428
+ const inventoryRoot = fullAiwgSourceRoot
429
+ ? srcRoot
430
+ : (!copyStandardSkills && isFullAiwgSourceRoot(configuredAiwgRoot) ? configuredAiwgRoot : null);
420
431
 
421
432
  console.log(`Deploying skills to Codex`);
422
433
  console.log(` Source: ${srcRoot}`);
@@ -440,8 +451,6 @@ function isFullAiwgSourceRoot(srcRoot) {
440
451
  // Codex normally deploys to the project `.agents/skills/` target, so the
441
452
  // kernel/standard split is enforced at filter time rather than via separate
442
453
  // destination directories. The standalone legacy default remains supported.
443
- const copyStandardSkills = cfg.copyStandardSkills === true;
444
-
445
454
  // Track every AIWG-managed source skill name so we can scope post-deploy
446
455
  // cleanup to skills AIWG ships — never delete user-authored or
447
456
  // third-party skills sitting alongside.
@@ -457,10 +466,13 @@ function isFullAiwgSourceRoot(srcRoot) {
457
466
  // so full-root cleanup can remove stale AIWG skills. Component-scoped
458
467
  // cleanup is limited below to names owned by that component, preserving
459
468
  // user-authored skills alongside the generated set.
460
- for (const { dir } of getSkillDirectories(srcRoot, 'all')) {
469
+ for (const { dir } of getSkillDirectories(inventoryRoot || srcRoot, 'all')) {
461
470
  const allSkills = findSkillDirs(dir);
462
471
  for (const s of allSkills) {
463
472
  allManagedNames.add(path.basename(s));
473
+ if (!copyStandardSkills && isKernelSkill(s)) {
474
+ desiredNames.add(path.basename(s));
475
+ }
464
476
  // Also record the frontmatter `name:` since Codex uses that as the
465
477
  // target dir. Best-effort — ignore parse errors.
466
478
  try {
@@ -469,7 +481,11 @@ function isFullAiwgSourceRoot(srcRoot) {
469
481
  if (fmMatch) {
470
482
  const nameMatch = fmMatch[1].match(/^\s*name:\s*(.+?)\s*$/m);
471
483
  if (nameMatch) {
472
- allManagedNames.add(stripWrappingQuotes(nameMatch[1]));
484
+ const deployedName = stripWrappingQuotes(nameMatch[1]);
485
+ allManagedNames.add(deployedName);
486
+ if (!copyStandardSkills && isKernelSkill(s)) {
487
+ desiredNames.add(deployedName);
488
+ }
473
489
  }
474
490
  }
475
491
  } catch { /* ignore */ }
@@ -528,7 +544,7 @@ function isFullAiwgSourceRoot(srcRoot) {
528
544
  // the .aiwg-managed marker. Treat only the exact historical names as
529
545
  // managed so malformed legacy frontmatter cannot survive an upgrade.
530
546
  let isAiwgManaged = allManagedNames.has(name) || LEGACY_RENAMED_SKILLS.has(name);
531
- if (!isAiwgManaged && fullAiwgSourceRoot) {
547
+ if (!isAiwgManaged && (fullAiwgSourceRoot || inventoryRoot)) {
532
548
  // Check for the .aiwg-managed marker file (preferred — survives
533
549
  // frontmatter transforms) or fall back to namespace check.
534
550
  const markerFile = path.join(target, name, '.aiwg-managed');